package vip.liux.contracts.security.sms;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.util.Assert;

import java.time.Duration;
import java.util.Map;

public class SpringCacheBasedCaptchaCache implements CaptchaCache {

    private static final Logger logger = LoggerFactory.getLogger(SpringCacheBasedCaptchaCache.class);

    private final Cache cache;

    public SpringCacheBasedCaptchaCache(Cache cache) {
        Assert.notNull(cache, "cache mandatory");
        this.cache = cache;
    }

    @Override
    public void putCodeInCache(String phone, String code, Duration expire) {
        logger.debug("Cache put: {}", phone);
        this.cache.put(phone, Map.of(
                "code", code,
                "times", "5",
                "expired", String.valueOf((System.currentTimeMillis() + expire.toMillis())))
        );
    }

    @Override
    public Map<String, String> getCodeFromCache(String phone) {
        Map<String, String> result = getCode(phone);
        if (result != null) {
            long expired = Long.parseLong(result.get("expired"));
            if (expired < System.currentTimeMillis()) {
                logger.debug("Cache expired: {}", phone);
                this.cache.evict(phone);
                return null;
            }
            int times = Integer.parseInt(result.get("times"));
            if (times <= 0) {
                cache.evict(phone);
            }
        }
        return result;
    }

    @Override
    public void errorTimesIncrement(String phone) {
        Map<String, String> result = getCode(phone);
        if (result != null) {
            int times = Integer.parseInt(result.get("times"));
            if (times > 0) {
                result.put("times", String.valueOf(times - 1));
                this.cache.put(phone, result);
            } else {
                this.cache.evict(phone);
            }
        } else {
            logger.warn("Cache not found: {}", phone);
        }
    }

    @Override
    public void removeCodeFromCache(String phone) {
        this.cache.evict(phone);
    }

    @SuppressWarnings("unchecked")
    private Map<String, String> getCode(String phone) {
        Cache.ValueWrapper element = (phone != null) ? this.cache.get(phone) : null;
        logger.debug("Cache hit: {}; phone: {}", element != null, phone);
        return (element != null) ? (Map<String, String>) element.get() : null;
    }
}
